# -*- coding: utf8 -*-

import os, md5

class Comparer(object):
    def __init__(self, length=False, datetime=False, contentMD5=False, contentFull=False):
        if not length and not datetime and not contentMD5 and not contentFull:
            raise ValueError("Didn't choose comparison method!")
        self.length = length
        self.datetime = datetime
        if contentMD5 and contentFull:
            raise ValueError("Content can be compared full or with MD5, not both!")
        elif contentMD5:
            self.content = "md5"
        elif contentFull:
            self.content = "full"
        else:
            self.content = False


class TreeComparer(object):
    def __init__(self, *args, **kwargs):
        self.comp = Comparer(*args, **kwargs)

    def compare(self, dir1, dir2):
        for x in self._searchLocal(dir1, dir2):
            yield x

    def _getFileType(self, arch):
        if os.path.isfile(arch):
            tipo = "file"
        elif os.path.isdir(arch):
            tipo = "dir"
        else:
            print "Debug Warning!! Unknown file type:", arch
            tipo = None
        return tipo

    def _searchLocal(self, dir1, dir2):
        listarchs1 = os.listdir(dir1)
        listarchs2 = os.listdir(dir2)
        for nombre in listarchs1:
            arch1 = os.path.join(dir1, nombre)
            arch2 = os.path.join(dir2, nombre)
            info1 = os.stat(arch1)
            tipo1 = self._getFileType(arch1)

            # only in the side 1
            if nombre not in listarchs2:
                if tipo1 == "file":
                    yield ("onlyin1", nombre, dir1, dir2, info1)
                elif tipo1 == "dir":
                    for x in self._goDeep(1, arch1):
                        yield x
                else:
                    raise ValueError("Invalid file type!")
                continue

            # in both, remove it from the second one to have then the rest
            listarchs2.remove(nombre)

            # compare them
            tipo2 = self._getFileType(arch2)
            info2 = os.stat(arch2)
            if tipo1 == "file" and tipo2 == "file":
                if self.comp.length or self.comp.datetime:
                    if self.comp.length:
                        if info1.st_size != info2.st_size:
                            yield ("different", nombre, dir1, dir2, info1, info2)
                            continue
                    if self.comp.datetime:
                        if info1.st_mtime != info2.st_mtime:
                            yield ("different", nombre, dir1, dir2, info1, info2)
                            continue
                
                if self.comp.content:
                    fh1 = open(arch1, "rb")
                    fh2 = open(arch2, "rb")
                    if self.comp.content == "md5":
                        cont1 = md5.new(fh1.read()).digest()
                        cont2 = md5.new(fh2.read()).digest()
                    elif self.comp.content == "full":
                        cont1 = fh1.read()
                        cont2 = fh2.read()
                    else:
                        raise ValueError("Invalid content type for comparison")
                    if cont1 != cont2:
                        yield ("different", nombre, dir1, dir2, info1, info2)
                        continue

                yield ("equal", nombre, dir1, dir2, info1, info2)

            elif tipo1 == "dir" and tipo2 == "dir":
                for x in self._searchLocal(arch1, arch2):
                    yield x

            elif tipo1 == "file" and tipo2 == "dir":
                yield ("different", nombre, dir1, dir2, info1, info2)
                for x in self._goDeep(2, arch2):
                    yield x
            elif tipo1 == "dir" and tipo2 == "file":
                yield ("different", nombre, dir1, dir2, info1, info2)
                for x in self._goDeep(1, arch1):
                    yield x
            else:
                raise ValueError("Invalid file type!")

        # all this is in the dir 2, and not in dir 1
        for nombre in listarchs2:
            arch2 = os.path.join(dir2, nombre)
            tipo2 = self._getFileType(arch2)
            info2 = os.stat(arch2)
            if tipo2 == "file":
                yield ("onlyin2", nombre, dir1, dir2, info2)
            elif tipo2 == "dir":
                for x in self._goDeep(2, arch2):
                    yield x
            else:
                raise ValueError("Invalid file type!")
            continue

    def _goDeep(self, lado, direct):
        listarchs = os.listdir(direct)
        for nombre in listarchs:
            arch = os.path.join(direct, nombre)
            tipo = self._getFileType(arch)
            info = os.stat(arch)
            if tipo == "file":
                if lado == 1:
                    yield ("onlyin%d"%lado, nombre, direct, None, info)
                elif lado == 2:
                    yield ("onlyin%d"%lado, nombre, None, direct, info)
                else:
                    raise ValueError("Incorrect value for 'side': %r" % lado)
            elif tipo == "dir":
                for x in self._goDeep(lado, arch):
                    yield x
            else:
                raise ValueError("Invalid file type!")
